package common

import (
	"sync"
	"sync/atomic"
)

type AsyncSeq struct {
	Tail *uint64
	Head *uint64

	Cond *sync.Cond
}

func NewAtomicSeq() *AsyncSeq {
	lock := new(sync.Mutex)
	return &AsyncSeq{
		Tail: new(uint64),
		Head: new(uint64),
		Cond: sync.NewCond(lock),
	}
}

func (a *AsyncSeq) Increase() uint64 {
	return atomic.AddUint64(a.Tail, 1)
}

func (a *AsyncSeq) Update(seq uint64) {
	a.Cond.L.Lock()
	if seq > atomic.LoadUint64(a.Head) {
		atomic.StoreUint64(a.Head, seq)
	}
	a.Cond.Broadcast()
	a.Cond.L.Unlock()
}

func (a *AsyncSeq) WaitDone(tail uint64) {
	if tail <= atomic.LoadUint64(a.Head) {
		return
	}
	a.Cond.L.Lock()
	for tail > atomic.LoadUint64(a.Head) {
		a.Cond.Wait()
	}
	a.Cond.L.Unlock()
}

func (a *AsyncSeq) LoadHead() uint64 {
	return atomic.LoadUint64(a.Head)
}

type LastIndex struct {
	sync.Mutex
	index *uint64
}

func NewLastIndex() *LastIndex {
	return &LastIndex{index: new(uint64)}
}

func (l *LastIndex) Get() uint64 {
	return atomic.LoadUint64(l.index)
}

func (l *LastIndex) Set(index uint64) {
	if index > atomic.LoadUint64(l.index) {
		l.Lock()
		if index > atomic.LoadUint64(l.index) {
			atomic.StoreUint64(l.index, index)
		}
		l.Unlock()
	}
}
